package com.zhangfb95.crosschain.server.controller;

import com.zhangfb95.crosschain.infra.model.Result;
import com.zhangfb95.crosschain.protocol.exc.request.GetBlockByNumberExcReq;
import com.zhangfb95.crosschain.protocol.exc.request.GetHeightExcReq;
import com.zhangfb95.crosschain.protocol.exc.request.GetTxByIdExcReq;
import com.zhangfb95.crosschain.protocol.exc.request.QueryExcReq;
import com.zhangfb95.crosschain.protocol.exc.request.SendExcReq;
import com.zhangfb95.crosschain.protocol.exc.response.GetBlockByNumberExcRes;
import com.zhangfb95.crosschain.protocol.exc.response.GetHeightExcRes;
import com.zhangfb95.crosschain.protocol.exc.response.GetTxByIdExcRes;
import com.zhangfb95.crosschain.protocol.exc.response.QueryExcRes;
import com.zhangfb95.crosschain.protocol.exc.response.SendExcRes;
import com.zhangfb95.crosschain.protocol.rpc.RequestHeaderConstants;
import com.zhangfb95.crosschain.server.infra.config.properties.WebServerProperties;
import com.zhangfb95.crosschain.server.infra.model.controller.request.rpcchain.RpcExecuteReq;
import com.zhangfb95.crosschain.server.infra.model.controller.request.rpcchain.RpcGetBlockByNumberReq;
import com.zhangfb95.crosschain.server.infra.model.controller.request.rpcchain.RpcGetHeightReq;
import com.zhangfb95.crosschain.server.infra.model.controller.request.rpcchain.RpcGetTxByIdReq;
import com.zhangfb95.crosschain.server.infra.model.controller.request.rpcchain.RpcQueryReq;
import com.zhangfb95.crosschain.server.infra.model.controller.request.rpcchain.RpcSendReq;
import com.zhangfb95.crosschain.server.infra.model.controller.response.rpcchain.RpcExecuteRes;
import com.zhangfb95.crosschain.server.infra.model.controller.response.rpcchain.RpcGetBlockByNumberRes;
import com.zhangfb95.crosschain.server.infra.model.controller.response.rpcchain.RpcGetHeightRes;
import com.zhangfb95.crosschain.server.infra.model.controller.response.rpcchain.RpcGetTxByIdRes;
import com.zhangfb95.crosschain.server.infra.model.controller.response.rpcchain.RpcListChainNameRes;
import com.zhangfb95.crosschain.server.infra.model.controller.response.rpcchain.RpcListChainPathRes;
import com.zhangfb95.crosschain.server.infra.model.controller.response.rpcchain.RpcQueryRes;
import com.zhangfb95.crosschain.server.infra.model.controller.response.rpcchain.RpcSendRes;
import com.zhangfb95.crosschain.server.service.ChainService;
import com.zhangfb95.crosschain.server.service.CrossChainService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author zhangfubing
 * @since 2023/12/4
 */
@RequestMapping("/rpc/chain")
@RestController
public class RpcChainController {

    @Autowired
    private WebServerProperties webServerProperties;
    @Autowired
    private ChainService chainService;
    @Autowired
    private CrossChainService crossChainService;

    @PostMapping("/listChainName")
    public Result<RpcListChainNameRes> listChainName() {
        List<String> chainNames = chainService.listChainName();
        return Result.ok(new RpcListChainNameRes().setChainNames(chainNames));
    }

    @PostMapping("/listChainPath")
    public Result<RpcListChainPathRes> listChainPath() {
        List<String> chainPaths = chainService.listChainPath();
        return Result.ok(new RpcListChainPathRes().setChainPaths(chainPaths));
    }

    @PostMapping("/getHeight")
    public Result<RpcGetHeightRes> getHeight(@RequestBody @Validated RpcGetHeightReq req) {
        GetHeightExcReq excReq = new GetHeightExcReq().setChainPath(req.getChainPath());
        GetHeightExcRes excRes = crossChainService.getHeight(excReq);
        RpcGetHeightRes res = new RpcGetHeightRes().setHeight(excRes.getHeight());
        return Result.ok(res);
    }

    @PostMapping("/getBlockByNumber")
    public Result<RpcGetBlockByNumberRes> getBlockByNumber(@RequestBody @Validated RpcGetBlockByNumberReq req) {
        GetBlockByNumberExcReq excReq = new GetBlockByNumberExcReq()
                .setChainPath(req.getChainPath())
                .setBlockNumber(req.getBlockNumber());
        GetBlockByNumberExcRes excRes = crossChainService.getBlockByNumber(excReq);
        RpcGetBlockByNumberRes res = new RpcGetBlockByNumberRes()
                .setBlockNumber(excRes.getBlockNumber())
                .setDataHash(excRes.getDataHash())
                .setBlockHash(excRes.getBlockHash())
                .setPreviousHash(excRes.getPreviousHash())
                .setBlockBytes(excRes.getBlockBytes())
                .setTxIds(excRes.getTxIds());
        return Result.ok(res);
    }

    @PostMapping("/getTxById")
    public Result<RpcGetTxByIdRes> getTxById(@RequestBody @Validated RpcGetTxByIdReq req) {
        GetTxByIdExcReq excReq = new GetTxByIdExcReq()
                .setChainPath(req.getChainPath())
                .setTxId(req.getTxId());
        GetTxByIdExcRes excRes = crossChainService.getTxById(excReq);
        RpcGetTxByIdRes res = new RpcGetTxByIdRes()
                .setTxId(excReq.getTxId())
                .setTransactionBytes(excRes.getTransactionBytes());
        return Result.ok(res);
    }

    @PostMapping("/query")
    public Result<RpcQueryRes> query(@RequestHeader(RequestHeaderConstants.USER_ID) String userId,
                                     @RequestBody @Validated RpcQueryReq req) {
        QueryExcReq excReq = new QueryExcReq()
                .setUserId(Long.valueOf(userId))
                .setServerId(webServerProperties.getServerId())
                .setChainPath(req.getChainPath())
                .setMethod(req.getMethod())
                .setArgs(req.getArgs());
        QueryExcRes excRes = crossChainService.query(excReq);
        RpcQueryRes res = new RpcQueryRes().setResult(excRes.getResult());
        return Result.ok(res);
    }

    @PostMapping("/send")
    public Result<RpcSendRes> send(@RequestHeader(RequestHeaderConstants.USER_ID) String userId,
                                   @RequestBody @Validated RpcSendReq req) {
        SendExcReq excReq = new SendExcReq()
                .setUserId(Long.valueOf(userId))
                .setServerId(webServerProperties.getServerId())
                .setChainPath(req.getChainPath())
                .setMethod(req.getMethod())
                .setArgs(req.getArgs());
        SendExcRes excRes = crossChainService.send(excReq);
        RpcSendRes res = new RpcSendRes()
                .setTransactionID(excRes.getTransactionID())
                .setResult(excRes.getResult());
        return Result.ok(res);
    }

    @PostMapping("/execute")
    public Result<RpcExecuteRes> execute(@RequestHeader(RequestHeaderConstants.USER_ID) String userId,
                                         @RequestBody @Validated RpcExecuteReq req) {
        // https://blog.csdn.net/yangyanping20108/article/details/131582501
        return Result.ok();
    }
}
